#include "fun.h"
#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <fstream>

const int NUM_COLOUR = 4;//Must bigger than 2

CSqure squre;

void InitSqure(CSqure& squre)
{
	squre.resize(X_WIDTH);
	for(size_t i = 0; i < squre.size(); ++i)
	{
		squre[i].resize(Y_WIDTH);
		for(size_t j = 0; j < squre[i].size(); ++j)
			squre[i][j] = 0;
	}
}

void CreateSqure(CSqure& squre)
{
	srand( (unsigned)time( NULL ) );
	for(size_t i = 0; i < squre.size(); ++i)
	{
		for(size_t j = 0; j < squre[i].size(); ++j)
		{
			if (squre[i][j] != 0)
				continue;

			int leftColour = 0;
			int upColour = 0;
			if (i > 1)
				leftColour = squre[i -1][j] & squre[i - 2][j];
			if (j > 1)
				upColour = squre[i][j - 1] & squre[i][j - 2];

			int r = rand() % NUM_COLOUR  + 1;
			while(r == upColour || r == leftColour)
				r = rand() % NUM_COLOUR  + 1;
			squre[i][j] = r;
		}
	}
}

int CheckSqure(const CSqure& squre, std::vector<CCoordinateGroup>& groups)
{
	size_t i, j, k;

	groups.clear();
	for(i = 0; i < squre.size(); ++i)
	{
		for(j = 0; j < squre[i].size(); ++j)
		{
			//If this cell is already in a found group, skip
			bool found = false;

			for(k = 0; k < groups.size(); ++k)
			{
				if (groups[k].Find(i, j))
					found = true;
			}

			if (found)
				continue;

			CCoordinateGroup tmpGroup;
			tmpGroup.push_back(std::make_pair<size_t, size_t>(i, j));
			int colour = squre[i][j];
			//Right direction
			for(k = j + 1; k < squre[i].size() && squre[i][k] == colour; ++k)
				tmpGroup.push_back(std::make_pair<size_t, size_t>(i, k));
			if (tmpGroup.size() < 3)
				tmpGroup.erase(tmpGroup.begin() + 1, tmpGroup.end());
			//Down direction
			size_t base = tmpGroup.size();
			for(k = i + 1; k < squre.size() && j < squre[k].size()&& squre[k][j] == colour; ++k)
				tmpGroup.push_back(std::make_pair<int, int>(k, j));
			if (tmpGroup.size() - base < 2)
				tmpGroup.erase(tmpGroup.begin() + base, tmpGroup.end());
			if (tmpGroup.size() < 3)
				tmpGroup.clear();
			//Put to groups
			if (tmpGroup.size() > 0)
				groups.push_back(tmpGroup);
		}
	}
	//Merge groups
	for(i = 0; i < groups.size(); ++i)
	{
		for(j = i + 1; j < groups.size(); ++j)
		{
			for(k = 0; k < groups[i].size(); ++k)
				if (groups[j].Find(groups[i][k].first, groups[i][k].second))
				{
					//Merge J to I
					groups[i].Merge(groups[j]);
					//Clear J
					groups[j].clear();
				}
		}
	}

	for(i = groups.size() - 1; i >= 0 && i < groups.size(); --i)
	{
		if (groups[i].size() == 0)
			groups.erase(groups.begin() + i);
	}

	return groups.size();
}

int ClickSqure(CSqure& squre, CCoordinate& swap1, CCoordinate& swap2)
{
	std::vector<CCoordinateGroup> tmp;
	for(int i = 0; i < squre.size(); ++i)
	{
		for(int j = 0; j < squre[i].size(); ++j)
		{
			//Check right side
			if (j < squre[i].size() - 1)
			{
				tmp.clear();
				std::swap(squre[i][j], squre[i][j + 1]);
				if (CheckSqure(squre, tmp) > 0)
				{
					//std::swap(squre[i][j], squre[i][j + 1]);
					swap1.first = i;
					swap1.second = j;
					swap2.first = i;
					swap2.second = j + 1;
					PrintSqure(squre, "After ClickSqure:");
					return 1;
				}
				std::swap(squre[i][j], squre[i][j + 1]);
			}
			//Check down side
			if (i < squre.size() - 1)
			{
				tmp.clear();
				std::swap(squre[i][j], squre[i + 1][j]);
				if (CheckSqure(squre, tmp) > 0)
				{
					//std::swap(squre[i][j], squre[i][j + 1]);
					swap1.first = i;
					swap1.second = j;
					swap2.first = i;
					swap2.second = j + 1;
					PrintSqure(squre, "After ClickSqure:");
					return 1;
				}
				std::swap(squre[i][j], squre[i + 1][j]);
			}
		}
	}
	return 0;
}

int ProcessSqure(CSqure& squre)
{
	std::vector<CCoordinateGroup> groups;
	if (CheckSqure(squre, groups) != 0)
	{
		size_t i, j;
		for(i = 0; i < groups.size(); ++i)
		{
			for(j = 0; j < groups[i].size(); ++j)
			{
				int x = groups[i][j].first;
				int y = groups[i][j].second;
				squre[x][y] = 0;
			}
		}
		PrintSqure(squre, "After Check:");
		MoveGroup(squre);
		groups.clear();
		PrintSqure(squre, "After MoveGroup:");
		CreateSqure(squre);
		PrintSqure(squre, "After Create:");
		return 1;
	}
	else
		return 0;
}

void MoveGroup(CSqure& squre)
{
	size_t j, k1, k2;
	for(j = 0; j < squre.size(); ++j)
	{
		//Sort for squr[k][j], 0 will be upper, stable sort
		for(k1 = 0; k1 < squre.size(); ++k1)
			for(k2 = k1 + 1; k2 < squre.size(); ++k2)
				if (squre[k1][j] != 0 && squre[k2][j] == 0)
					std::swap(squre[k1][j], squre[k2][j]);
	}
}

int PrintSqure(CSqure& squre, const char * promot)
{
	std::ofstream ofs("tst.txt", std::ios_base::app | std::ios_base::out);
	std::ostream& ostrm = ofs;
	if (NUM_COLOUR > 4)
		return -2;

	if (promot)
		ostrm << promot << "\n";
	for(size_t i = 0; i < squre.size(); ++i)
	{
		for(size_t j = 0; j < squre[i].size(); ++j)
		{
			switch (squre[i][j])
			{
			case 0: ostrm << "  "; break;
			case 1: ostrm << "1 "; break;
			case 2: ostrm << "2 "; break;
			case 3: ostrm << "3 "; break;
			case 4: ostrm << "4 "; break;
			default:
				return -1;
			}
		}
		ostrm << "\n";
	}
	ostrm << "\n";
	//system("pause");
	//system("cls");
	return 0;
}

bool CCoordinateGroup::Find(size_t x, size_t y)
{
	CCoordinateGroup& self = *this;
	for(int i = 0; i < self.size(); ++i)
		if (self[i].first == x && self[i].second == y)
			return true;
	return false;
}

void CCoordinateGroup::Merge(const CCoordinateGroup& other)
{
	std::vector<CCoordinate>::iterator iter;
	//Merge directly
	this->insert(this->end(), other.begin(), other.end());
	//Remove duplicated elements
	iter = this->begin() + other.size();
	while (iter != this->end())
	{
		if (this->Find(iter->first, iter->second))
			iter = this->erase(iter);
		else
			++iter;
	}
}